﻿using TribesPSO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;

namespace TribesPSOTest
{


    /// <summary>
    ///This is a test class for SearchSpace and is intended
    ///to contain all SearchSpace Unit Tests for methods with implementations in the base class and 
    ///to provide a base class for test of derviced SearchSpace implementations
    ///</summary>
    [TestClass()]
    public abstract class SearchSpaceTest<TParticle> where TParticle : Particle
    {
        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        // 
        //You can use the following additional attributes as you write your tests:
        //
        //Use ClassInitialize to run code before running the first test in the class
        //[ClassInitialize()]
        //public static void MyClassInitialize(TestContext testContext)
        //{
        //}
        //
        //Use ClassCleanup to run code after all tests in a class have run
        //[ClassCleanup()]
        //public static void MyClassCleanup()
        //{
        //}
        //
        //Use TestInitialize to run code before running each test
        //[TestInitialize()]
        //public void MyTestInitialize()
        //{
        //}
        //
        //Use TestCleanup to run code after each test has run
        //[TestCleanup()]
        //public void MyTestCleanup()
        //{
        //}
        //
        #endregion


        protected abstract SearchSpace<TParticle> CreateSearchSpace();

        protected SearchSpace_Accessor<TParticle> CreateSearchSpace_Accessor()
        {
            SearchSpace<TParticle> s = CreateSearchSpace();
            PrivateObject obj = new PrivateObject(s);

            return new SearchSpace_Accessor<TParticle>(obj);
        }

        /// <summary>
        ///A test for GenerateNewParticle
        ///</summary>
        [TestMethod()]
        public void GenerateNewParticleTest()
        {
            SearchSpace_Accessor<TParticle> target = CreateSearchSpace_Accessor();

            Particle actual;
            actual = target.GenerateNewParticle();

            Assert.IsNotNull(actual);
        }

        /// <summary>
        ///A test for GeneratePosition
        ///</summary>
        [TestMethod()]
        public void GeneratePositionTest()
        {
            SearchSpace_Accessor<TParticle> target = CreateSearchSpace_Accessor();

            EuclidianVector actual;
            actual = target.GeneratePosition();

            Assert.IsNotNull(actual);
        }

        /// <summary>
        ///A test for GenerateParticleAtPosition
        ///</summary>
        [TestMethod()]
        public void GenerateParticleAtPositionTest()
        {
            SearchSpace_Accessor<TParticle> target = CreateSearchSpace_Accessor();

            EuclidianVector expectedPosition = target.GeneratePosition();

            Particle actual = target.GenerateParticleAtPosition(expectedPosition);

            Assert.AreEqual<EuclidianVector>(expectedPosition, actual.Position);
        }

        /// <summary>
        ///A test for GenerateNewParticle
        ///</summary>
        [TestMethod()]
        public void GenerateMultipleNewParticleTest()
        {
            SearchSpace_Accessor<TParticle> target = CreateSearchSpace_Accessor();

            System.Random rng = new System.Random();

            for (int n = 0; n < 10; n++)
            {
                int numberToGenerate = rng.Next(50);

                IEnumerable<Particle> actual = target.GenerateNewParticle(numberToGenerate);

                foreach (Particle p in actual)
                {
                    Assert.IsNotNull(p);
                }
                Assert.AreEqual(numberToGenerate, actual.Count());
            }
        }

        /// <summary>
        ///A test for MoveThenAdapt
        ///</summary>
        [TestMethod()]
        public void MoveThenAdaptTest()
        {
            SearchSpace<TParticle> target = CreateSearchSpace();
            bool moved = false;
            target.SwarmMoving += new EventHandler(delegate { moved = true; });

            //Check that the first time we call moveThanAdapt that we seed the search space but don't
            //actually move
            Assert.AreEqual(0, target.SwarmSize);
            target.MoveThenAdapt();
            Assert.AreEqual(1, target.SwarmSize);
            Assert.IsFalse(moved);

            target.MoveThenAdapt();
            Assert.IsTrue(moved);
        }

        /// <summary>
        ///A test for BestSolution
        ///</summary>
        [TestMethod()]
        public void BestSolutionTest()
        {
            SearchSpace<TParticle> target = CreateSearchSpace();
            Solution actual;

            //The first time through, this should be null because the search space shouldn't have any particles
            actual = target.BestSolution;
            Assert.AreEqual(null, actual);

            target.MoveThenAdapt();

            Solution expected = new Solution(target.ParticlePositions.Single(), target.GoodnessFunction);
            actual = target.BestSolution;

            //This time through, the best solution should be the system's only solution
            Assert.AreEqual(expected.Position, actual.Position);
        }
    }
}
